-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Move git config/remote to gitrepo package and add global lock to resolve possible conflict when updating repository git config file #35151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…lve possible conflict when updating repository git config file
No, it doesn't fix #32018 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't see why it is worth or right to do so, and don't see why it fixes that issue
In cases where |
So it is an improvement but doesn't "Fix #32018", right? But does it really need the "global lock"? Is it possible that git already uses "*.lock" files by |
The Git command-line interface is primarily designed for human interaction. When a git config write operation is initiated and a config.lock file is created, any subsequent git config write operation will immediately fail instead of waiting for the first one to complete. While I couldn’t find official documentation confirming this behavior, the implementation can be found in the Git source code here: Based on this, it appears that Git does not implement a locking mechanism that causes subsequent operations to wait—it simply fails fast. I believe this change partially addresses #32018, as there are three possible causes for the issue:
|
Git already does a file-based "global lock"
Update: git passes timeout=0, so it only locks "once" |
…a into lunny/fix_git_config_conflict
last call @go-gitea/technical-oversight-committee |
modules/gitrepo/config.go
Outdated
// No lock is needed because the remote remoteName will be checked before invoking this function. | ||
// Then it will not update the remote automatically if the remote does not exist. | ||
func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { | ||
return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and above, why git remote update/prune
belongs to "config.go"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modules/gitrepo/config.go
Outdated
releaser, err := globallock.Lock(ctx, getRepoConfigLockKey(repo.RelativePath())) | ||
if err != nil { | ||
return err | ||
} | ||
defer releaser() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these "lock" related code could be in a helper wrapper function:
gitConfigLock(..., func() {
...
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modules/gitrepo/config.go
Outdated
if addr == "" { | ||
return nil, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this design is problematic.
Developers have been used to that "if err == nil, then there is a non-nil return value"
If you do so, developers would forget to check "u == nil" frequently, and the nil check introduces unnecessary code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
services/mirror/mirror_pull.go
Outdated
if err != nil && !git.IsRemoteNotExistError(err) { | ||
return err | ||
} | ||
|
||
cmd := git.NewCommand("remote", "add").AddDynamicArguments(remoteName).AddArguments("--mirror=fetch").AddDynamicArguments(addr) | ||
_, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath}) | ||
err = gitrepo.GitRemoteAdd(ctx, repo, remoteName, addr, "--mirror=fetch") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and other places, IIRC you have defined consts, but why still use string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
services/mirror/mirror_pull.go
Outdated
Stderr: &stderrBuilder, | ||
}); err != nil { | ||
|
||
// check whether the remote still exists before pruning to avoid prune creating a new remote |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid prune creating a new remote
What "prune"? "git remote prune" or "git remote update --prune"?
I see neither of them would "create a new remote" in the git manual. Correct me if I was wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…a into lunny/fix_git_config_conflict
services/mirror/mirror_pull.go
Outdated
// check whether the remote still exists before remote update prune | ||
// this is needed because prune will not fail if the remote does not exist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What "prune"? "git remote prune" or "git remote update --prune"?
- below does
git remote update --prune
pruneBrokenReferences
doesgit remote prune
So what are you doing here for?
I see NEITHER of them would "create a new remote" in the git manual. Correct me if I was wrong.
Why "this is needed because prune will not fail if the remote does not exist" is a problem?
Why you need this logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It said before remote update prune ..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It said
before remote update prune ..
Why it needs this logic? What wrong would happen if remote doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the comment via 38c19d2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it needs this logic? What wrong would happen if remote doesn't exist?
Why the "remote" might not exist?
What's wrong with "it will appear to succeed silently, even though nothing was updated."?
If you return "false" here, what end users can see or can do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remove these lines via a9633fd
Partially fix #32018
git config
andgit remote
write operations create a temporary file namedconfig.lock
. Since these operations are not atomic, they must not be run in parallel. If two requests attempt to modify the same repository concurrently—such as during a compare operation—one may fail due to the presence of an existingconfig.lock
file.In cases where
config.lock
is left behind due to an unexpected program exit, a global lock mechanism could allow us to safely remove the stale lock file when a related error is detected. While this behavior is not yet implemented in this PR, it is planned for a future enhancement.